/*
==========================================================================
File:        ex2.c (skeleton)
Version:     4, 20/1/2011
Authors:     Toby Howard
==========================================================================
*/

/* The following ratios are not to scale: */
/* Moon orbit : planet orbit */
/* Orbit radius : body radius */
/* Sun radius : planet radius */

#include <GL/glut.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "frames.h"

#define MAX_BODIES 20
#define TOP_VIEW 1
#define ECLIPTIC_VIEW 2
#define SHIP_VIEW 3
#define EARTH_VIEW 4
#define PI 3.14159
#define DEG_TO_RAD 0.017453293
#define ORBIT_POLY_SIDES 40
#define TIME_STEP 0.5   /* days per frame */
#define TURN_ANGLE 4.0
#define RUN_SPEED  1000

GLfloat  eyex,    eyey,    eyez;    /* Eye point                */
GLfloat  centerx, centery, centerz; /* Look point               */
GLfloat  upx,     upy,     upz;     /* View up vector           */
GLdouble lat,     lon;
GLdouble mlat,    mlon;
GLint width= 640, height= 480;

typedef struct { 
  char    name[20];       /* name */
  GLfloat r, g, b;        /* colour */
  GLfloat orbital_radius; /* distance to parent body (km) */
  GLfloat orbital_tilt;   /* angle of orbit wrt ecliptic (deg) */
  GLfloat orbital_period; /* time taken to orbit (days) */
  GLfloat radius;         /* radius of body (km) */
  GLfloat axis_tilt;      /* tilt of axis wrt body's orbital plane (deg) */
  GLfloat rot_period;     /* body's period of rotation (days) */
  GLint   orbits_body;    /* identifier of parent body */
  GLfloat spin;           /* current spin value (deg) */
  GLfloat orbit;          /* current orbit value (deg) */
 } body;

body  bodies[MAX_BODIES];
int   numBodies, current_view, draw_labels, draw_orbits, draw_starfield;

typedef struct{
	int coordinate[1000][3];
	int x;
	int y;
	int z;
} stars;

float date;

/*****************************/
//int [][]buildStars(void){
//	stars starField;
//	int i;
//	for(i = 0; i<1000; i++){
//		for(j = 0; j<3; j++){
//			starField[i][j]
//		}
float myRand (void)
{
  /* return a random float in the range [0,1] */

  return (float) rand() / RAND_MAX;
}

/*****************************/

void drawStarfield (void)
{
  /* This is for you to complete. */
  glBegin (GL_POINTS);
    int i;
    GLint a,b,c,a1,b1,c1,a2,b2,c2,a3,b3,c3,a4,b4,c4,a5,b5,c5,a6,b6,c6,a7,b7,c7;
    for(i= 0 ; i< 1000; i++){
        a = -900000000 * myRand(); 
        b = 900000000 * myRand(); 
        c = -900000000 * myRand();

        a1 = 900000000 * myRand(); 
        b1 = 900000000 * myRand(); 
        c1 = 900000000 * myRand();

        a2 = -900000000 * myRand(); 
        b2 = 900000000 * myRand(); 
        c2 = 900000000 * myRand(); 

        a3 = 900000000 * myRand(); 
        b3 = 900000000 * myRand(); 
        c3 = -900000000 * myRand();

        a4 = -900000000 * myRand(); 
        b4 = -900000000 * myRand(); 
        c4 = -900000000 * myRand();

        a5 = 900000000 * myRand(); 
        b5 = -900000000 * myRand(); 
        c5 = 900000000 * myRand();

        a6 = -900000000 * myRand(); 
        b6 = -900000000 * myRand(); 
        c6 = 900000000 * myRand(); 

        a7 = 900000000 * myRand(); 
        b7 = -900000000 * myRand(); 
        c7 = -900000000 * myRand();  
        glVertex3f (a,b,c);
        glVertex3f (a1,b1,c1);
        glVertex3f (a2,b2,c2);
        glVertex3f (a3,b3,c3);

        glVertex3f (a4,b4,c4);
        glVertex3f (a5,b5,c5);
        glVertex3f (a6,b6,c6);
        glVertex3f (a7,b7,c7);
    }
   /* and all the other points */
  glEnd ();

}

/*****************************/

void readSystem(void)
{
  /* reads in the description of the solar system */

  FILE *f;
  int i;

  f= fopen("sys", "r");
  if (f == NULL) {
     printf("ex2.c: Couldn't open the datafile 'sys'\n");
     printf("To get this file, use the following command:\n");
     printf("  cp /opt/info/courses/COMP27112/ex2/sys .\n");
     exit(0);
  }
  fscanf(f, "%d", &numBodies);
  for (i= 0; i < numBodies; i++)
  {
    fscanf(f, "%s %f %f %f %f %f %f %f %f %f %d", 
      bodies[i].name,
      &bodies[i].r, &bodies[i].g, &bodies[i].b,
      &bodies[i].orbital_radius,
      &bodies[i].orbital_tilt,
      &bodies[i].orbital_period,
      &bodies[i].radius,
      &bodies[i].axis_tilt,
      &bodies[i].rot_period,
      &bodies[i].orbits_body);

    /* Initialise the body's state */
    bodies[i].spin= 0.0;
    bodies[i].orbit= myRand() * 360.0; /* Start each body's orbit at a
                                          random angle */
    bodies[i].radius*= 1000.0; /* Magnify the radii to make them visible */
  }
  fclose(f);
}

/*****************************/

void drawString (void *font, float x, float y, char *str)
{ /* Displays the string "str" at (x,y,0), using font "font" */

  /* This is for you to complete. */

}

/*****************************/

void showDate(void)
{ /* Displays the current date */

 /* This is for you to complete. */
}

/*****************************/

void setView (void) {
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  switch (current_view) {
  case TOP_VIEW:
    /* This is for you to complete. */
    eyez = eyex = 0;
    eyey = 6*pow(10,8);
    centerx=centery=centerz=0;
    upy = upz = 0;
    upz = 1;
		  //upx = cos((lat+mlat)*DEG_TO_RAD)*sin((lon+mlon)*DEG_TO_RAD);
//		  upy = sin((lat+mlat)*DEG_TO_RAD);
//		  upz = cos((lat+mlat)*DEG_TO_RAD)*cos((lon+mlon)*DEG_TO_RAD);
//		  centerx = eyex + upx; 
//		  centery = eyey + upy; 
//		  centerz = eyez + upz;
    break;  
  case ECLIPTIC_VIEW:
    eyey = eyex = 0;
    eyez = 3*pow(10,8);
    centerx=centery=centerz=0;
	upz = upx = 0;
    upy = 1;
		  //centerx = eyex + upx; 
//		  centery = eyey + upy; 
//		  centerz = eyez + upz;
    break;
    /* This is for you to complete. */
    break;  
  case SHIP_VIEW:
    eyey = 99990000;
    eyex = 99990000;
    eyez = 99990000;
    centerx=centery=centerz=0;
    upx = -1;
    upz = -1;
    upy = 1;
		  //centerx = eyex + upx; 
//		  centery = eyey + upy; 
//		  centerz = eyez + upz;
    /* This is for you to complete. */    
    break;  
  case EARTH_VIEW: 
    /* This is for you to complete. */
    break;  
  }
}

/*****************************/

void menu (int menuentry) {
  switch (menuentry) {
  case 1: current_view= TOP_VIEW;
          break;
  case 2: current_view= ECLIPTIC_VIEW;
          break;
  case 3: current_view= SHIP_VIEW;
          break;
  case 4: current_view= EARTH_VIEW;
          break;
  case 5: draw_labels= !draw_labels;
          break;
  case 6: draw_orbits= !draw_orbits;
          break;
  case 7: draw_starfield= !draw_starfield;
          break;
  case 8: exit(0);
  }
}

/*****************************/
void drawAxis()
{
  glBegin(GL_LINE_STRIP);
       glVertex3f(0.0,0.0,10*bodies[0].radius);
       glVertex3f(0.0,0.0,-10*bodies[0].radius); 
  glEnd();

  glBegin(GL_LINE_STRIP);
       glVertex3f(0.0,10*bodies[0].radius,0.0);
       glVertex3f(0.0,-10*bodies[0].radius,0.0); 
  glEnd();

  glBegin(GL_LINE_STRIP);
       glVertex3f(10*bodies[0].radius,0.0,0.0);
       glVertex3f(-10*bodies[0].radius,0.0,0.0); 
  glEnd();
}

void init(void)
{
  /* Define background colour */
  glClearColor(0.0, 0.0, 0.0, 0.0);
  
  eyex=  0.0; /* Set eyepoint at eye height within the scene */
  eyey=  1.7;
  eyez= 10.0;

  upx= 0.0;   /* Set up direction to the +Y axis  */
  upy= 10.0;
  upz= 0;
     
  lat = 0.0;
  lon = 0.0;
  mlat = 0.0;
  mlon = 0.0;
  
  centerx=centery=centerz=0;
  
  glutCreateMenu (menu);
  glutAddMenuEntry ("Top view", 1);
  glutAddMenuEntry ("Ecliptic view", 2);
  glutAddMenuEntry ("Spaceship view", 3);
  glutAddMenuEntry ("Earth view", 4);
  glutAddMenuEntry ("", 999);
  glutAddMenuEntry ("Toggle labels", 5);
  glutAddMenuEntry ("Toggle orbits", 6);
  glutAddMenuEntry ("Toggle starfield", 7);
  glutAddMenuEntry ("", 999);
  glutAddMenuEntry ("Quit", 8);
  glutAttachMenu (GLUT_RIGHT_BUTTON);

  current_view= TOP_VIEW;
  draw_labels= 1;
  draw_orbits= 1;
  //draw_starfield= 1;
}

/*****************************/

void animate(void)
{
  int i;

    date+= TIME_STEP;

    for (i= 0; i < numBodies; i++)  {
      bodies[i].spin += 360.0 * TIME_STEP / bodies[i].rot_period;
      bodies[i].orbit += 360.0 * TIME_STEP / bodies[i].orbital_period;
      glutPostRedisplay();
    }

}

/*****************************/

void drawOrbit (int n)
{ /* Draws a polygon to approximate the circular
     orbit of body "n" */
    int i;
    int in = 360 / ORBIT_POLY_SIDES;
    /* This is for you to complete. */
    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    //glBegin(GL_POLYGON);
    glBegin(GL_LINE_LOOP);
       for(i=0; i<360; i++){
	   glVertex3f(cos(i*DEG_TO_RAD)*bodies[n].orbital_radius, 
		      0.0,sin(i*DEG_TO_RAD)*bodies[n].orbital_radius);
	   /*if (n == 5) {
	       glVertex3f((cos(bodies[3].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius+
			   cos(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius)*cos(i*in*DEG_TO_RAD), 
			   (sin(bodies[3].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius+
			    sin(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius)*cos(i*in*DEG_TO_RAD), 
			   (cos(bodies[3].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius+
			    cos(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius)*sin(i*in*DEG_TO_RAD));

	    }
	    else if (n == 6) {
		glVertex3f((cos(bodies[4].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius+
			    cos(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius)*cos(i*in*DEG_TO_RAD), 
			   (sin(bodies[4].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius+
			    sin(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius)*cos(i*in*DEG_TO_RAD), 
			   (cos(bodies[4].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius+
			    cos(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius)*sin(i*in*DEG_TO_RAD));


	    }
	    else{*/
	      glVertex3f(cos(bodies[n].orbital_tilt*DEG_TO_RAD)*cos(i*in*DEG_TO_RAD)*bodies[n].orbital_radius, 
			 sin(bodies[n].orbital_tilt*DEG_TO_RAD)*cos(i*in*DEG_TO_RAD)*bodies[n].orbital_radius, 
			 cos(bodies[n].orbital_tilt*DEG_TO_RAD)*sin(i*in*DEG_TO_RAD)*bodies[n].orbital_radius);
	    /*}*/
      }
    glEnd ();    
}

/*****************************/

void drawLabel(int n)
{ /* Draws the name of body "n" */

    /* This is for you to complete. */
}

/*****************************/

void drawBody(int n)
{
 /* Draws body "n" */

    /* This is for you to complete. */
  //glColor3f(n,n+1,n+2);
  if( n == 5){
	  if (draw_orbits) {
		  drawOrbit(n);
	  }
      
      glRotatef(90, 1.0, 0.0, 0.0);
      //Tspin
      /*if (n != 0) {
		  glRotatef(bodies[n].spin, 0.0, 0.0, 1.0);
      }*/

      glRotatef(bodies[n].axis_tilt, 1.0, 0.0, 0.0);
      //Torb

      glTranslatef(cos(bodies[3].orbital_tilt*DEG_TO_RAD)*bodies[3].orbital_radius +
                   cos(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius, 
                   sin(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius, 0.0);
      
      glBegin(GL_LINE_STRIP);
              glVertex3f(0.0,0.0,2*bodies[n].radius);
              glVertex3f(0.0,0.0,-2*bodies[n].radius); 
      glEnd();
      
      glutWireSphere((bodies[n].radius), 20, 20);

  }
  if( n == 6){
      if (draw_orbits) {
          drawOrbit(n);
      }
      
      glRotatef(90, 1.0, 0.0, 0.0);
      //Tspin
      /*if (n != 0) {
	      glRotatef(bodies[n].spin, 0.0, 0.0, 1.0);
      }*/
	  
      glRotatef(bodies[n].axis_tilt, 1.0, 0.0, 0.0);
      //Torb

      glTranslatef(cos(bodies[4].orbital_tilt*DEG_TO_RAD)*bodies[4].orbital_radius +
                   cos(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius, 
                   sin(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius, 0.0);
     
      glBegin(GL_LINE_STRIP);
              glVertex3f(0.0,0.0,2*bodies[n].radius);
              glVertex3f(0.0,0.0,-2*bodies[n].radius); 
      glEnd();

      glutWireSphere((bodies[n].radius), 20, 20);

  }
  else{
      if (draw_orbits) {
	  drawOrbit(n);
      }

      glRotatef(90, 1.0, 0.0, 0.0);
      //Tspin
      /*if (n != 0) {
	 glRotatef(bodies[n].spin, 0.0, 0.0, 1.0);
      }*/	  

      glRotatef(bodies[n].axis_tilt, 1.0, 0.0, 0.0);
      //Torb
      
      glTranslatef(cos(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius, 
                   sin(bodies[n].orbital_tilt*DEG_TO_RAD)*bodies[n].orbital_radius, 0.0);
     
      glBegin(GL_LINE_STRIP);
              glVertex3f(0.0,0.0,10*bodies[n].radius);
              glVertex3f(0.0,0.0,-10*bodies[n].radius); 
      glEnd();
     
      glutWireSphere((bodies[n].radius), 20, 20);
  }
  
}

/*****************************/

void display(void)
{
  int i;

  glClear(GL_COLOR_BUFFER_BIT);

  showDate();

  /* set the camera */
  setView();

  gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz);

	if (draw_starfield)
	    drawStarfield();

  drawAxis();

  for (i= 0; i < numBodies; i++)
  {
    glPushMatrix();
      drawBody (i);
    glPopMatrix();
  }

  glutSwapBuffers();
}

/*****************************/

void reshape(int w, int h)
{
  glViewport(0, 0, (GLsizei) w, (GLsizei) h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective (48.0, (GLfloat) w/(GLfloat) h, 10000.0, 800000000.0);
  width= w;   /* Record the new width and height */
  height= h;
}
  
/*****************************/
void mouse_motion(int x, int y) {
	
	/* To be completed*/ 
	mlon = (-100.0/width)*x + 50;
	mlat = (-100.0/height)*y + 50; 
	
} // mouse_motion()
void keyboard(unsigned char key, int x, int y)
{
  switch (key)
  {    
	case 27:  /* Escape key */
      exit(0);
  }
} 
void cursor_keys(int key, int x, int y) {
   switch (key) {
		   GLfloat updirx;
		   GLfloat updirz;
		   GLfloat downdirx;
		   GLfloat downdirz;

		   /* To be completed */
	   case GLUT_KEY_LEFT:
		   lon = lon + TURN_ANGLE;
		   break;

	   case GLUT_KEY_RIGHT:
		   lon = lon - TURN_ANGLE;
		   break;

	   case GLUT_KEY_PAGE_UP:
		   lat = lat + TURN_ANGLE;

		   if(lat >= 90){
			   lat = 89;
		   }
		   else if(lat <= -90){
			   lat = -89;
		   }

		   break;

	   case GLUT_KEY_PAGE_DOWN:
		   lat = lat - TURN_ANGLE;

		   if(lat >= 90){
			   lat = 89;
		   }
		   else if(lat <= -90){
			   lat = -89;
		   }

		   break;

	   case GLUT_KEY_HOME:
		   init();
		   break;

	   case GLUT_KEY_UP:

		   updirx = RUN_SPEED * sin(lon*DEG_TO_RAD);
		   updirz = RUN_SPEED * cos(lon*DEG_TO_RAD);

		   eyex = eyex + updirx; 
		   eyez = eyez + updirz;
		   break;


	   case GLUT_KEY_DOWN:

		   downdirx = RUN_SPEED * sin(lon*DEG_TO_RAD);
		   downdirz = RUN_SPEED * cos(lon*DEG_TO_RAD);

		   eyex = eyex - downdirx; 
		   eyez = eyez - downdirz;
		   break;

   }
} // cursor_keys()
/*****************************/

int main(int argc, char** argv)
{
  glutInit (&argc, argv);
  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
  glutCreateWindow ("COMP27112 Exercise 2");
  glutFullScreen();
  init ();
  glutDisplayFunc (display); 
  glutReshapeFunc (reshape);
  glutKeyboardFunc (keyboard);
  glutSpecialFunc (cursor_keys);
  glutPassiveMotionFunc (mouse_motion);
  glutIdleFunc (animate);
  readSystem();
  //draw_starfield = 1;
  glutMainLoop ();
  return 0;
}
/* end of ex2.c */
